#include <OgreVector3.h>
#include <irrKlang.h>

const float PI = 3.1415926535897932385f;
const float PI_H = PI/2;
const float PI_T = PI/3;
const float PI_Q = PI/4;
const float PI_S = PI/6;
const float E = 2.7182818284590452354f;
const float GR = 1.6180339887498948482f;
const float LN2 = 0.69314718055994530942f;
const float LN3 = 1.0986122886681096914f;
const float SQRT2 = 1.4142135623730950488f;
const float SQRT3 = 1.7320508075688772935f;
const float NEG_ONE_THREE_F = -.33333333333333333333333333333f;
const float NEG_TWO_THREE_F = -.66666666666666666666666666667f;
const float HALF_F = 0.5f;
const float ONE_F = 1.0f;
const float ONE_HALF_F = 1.5; 
const float TWO_F = 2.0f;


using namespace irrklang;
class PodMath
{
public:

	PodMath();
	~PodMath();

	struct pVec2
	{
	private:
		PodMath *podmath;
	public:
		float x;
		float y;
		void initialize(float i = 0.0f, float j = 0.0f) { x = i; y = j; podmath = new PodMath();}

		float getLength(void)
		{
			return podmath->fast_sqrt((x*x)+(y*y));
		}
		
		float getDistance(PodMath::pVec2 *vec2)
		{
			float distance;
			PodMath::pVec2 *newvec = new PodMath::pVec2();
			newvec->initialize(x - vec2->x, y - vec2->y);
			distance = newvec->getLength();
			return abs(distance);
		}
	};

	struct pVec3
	{
	private:
		PodMath *podmath;
	public:
		float x;
		float y;
		float z;

		void initialize(float i = 0.0f, float j = 0.0f, float k = 0.0f) { x = i; y = j; z = k; podmath = new PodMath();}

		float getLength(void) // Gets the length of the vector.
		{
			return podmath->fast_sqrt((x*x)+(y*y)+(z*z));
		}
		
		float getDistance(PodMath::pVec3 *vec2) // Gets the distance between two points, its (Vec1 - Vec2)
		{
			float distance;
			PodMath::pVec3 *newvec = new PodMath::pVec3();
			newvec->initialize(x - vec2->x, y - vec2->y, z - vec2->z);
			distance = newvec->getLength();
			return abs(distance);
		}		

		void scalarAdd(float n) { x += n; y += n; z += n; }
		void scalarSub(float n) { x -= n; y -= n; z -= n; }
		void scalarMult(float n) { x *= n; y *= n; z *= n; }
		void scalarDiv(float n) { x /= n; y /= n; z /= n; }
		void scalarExp(float n) { x = pow(x, n); y = pow(y, n); z = pow(z, n); }
		void scalarRoot(float n) { x = pow(x, 1/n); y = pow(y, 1/n); z = pow(z, 1/n); }

	};

	struct pMatrix2D
	{
		float mat[2][2];

		template <class _T>
		void initialize(_T r1n1, _T r1n2, _T r2n1, _T r2n2)
		{
			mat[0][0] = (float)r1n1;
			mat[0][1] = (float)r1n2;
			mat[1][0] = (float)r2n1;
			mat[1][1] = (float)r2n2;
		}

		template <class _T>
		void initilizeFrom1DArray(_T row1[2], _T row2[2])
		{
			mat[0][0] = (float)row1[0];
			mat[0][1] = (float)row1[1];
			mat[1][0] = (float)row2[0];
			mat[1][1] = (float)row2[1];
		}

		float determinate(void)
		{
			return mat[0][0]*mat[1][1] - 
				   mat[0][1]*mat[1][0];
		}
	};

	struct pMatrix3D
	{
		float row1[3];
		float row2[3];
		float row3[3];
		float mat[3][3];

		template <class _T>
		void initialize(_T r1n1, _T r1n2, _T r1n3, _T r2n1, _T r2n2, _T r2n3, _T r3n1, _T r3n2, _T r3n3)
		{
			mat[0][0] = (float)r1n1;
			mat[0][1] = (float)r1n2;
			mat[0][2] = (float)r1n3; 
			mat[1][0] = (float)r2n1;
			mat[1][1] = (float)r2n2;
			mat[1][2] = (float)r2n3;
			mat[2][0] = (float)r3n1;
			mat[2][1] = (float)r3n2;
			mat[2][2] = (float)r3n3;
		}

		template <class _T>
		void initializeFrom1DArray(_T row1[3], _T row2[3], _T row3[3])
		{
			for (int i = 0; i < 3; i++)
			{
				mat[0][i] = (float)row1[i];
				mat[1][i] = (float)row2[i];
				mat[2][i] = (float)row3[i];
			}
		}

		float determinate()
		{
			return mat[0][0]*mat[1][1]*mat[2][2] + 
				   mat[0][1]*mat[1][2]*mat[2][0] + 
				   mat[0][2]*mat[1][0]*mat[2][1] - 
				   mat[0][2]*mat[1][1]*mat[2][0] - 
				   mat[0][1]*mat[1][0]*mat[2][2] -
				   mat[0][0]*mat[1][2]*mat[2][1];
		}
	};
	

	//Vector Conversions
	PodMath::pVec3* OgreVectorToPVec(Ogre::Vector3 vec);
	PodMath::pVec3* IrrKlangVectorToPvec(irrklang::vec3d<float> vec);
	irrklang::vec3d<float> PVecToIrrKlangVector(PodMath::pVec3* vec);
	Ogre::Vector3 PVecToOgreVector(PodMath::pVec3* vec);
	irrklang::vec3d<float> OgreVectorToIrrKlangVector(Ogre::Vector3 vec);
	Ogre::Vector3 IrrKlangVectorToOgreVector(vec3d<float> vec);
	//End Vector Conversions

	//Comparisons
	template <class _T>
	_T getMax(_T x, _T y)
	{
		return (_T) (x > y) ? x : y; // Notice: May not need (_T)
	}

	template <class _T>
	_T getMin(_T x, _T y)
	{
		return (_T) (x < y) ? x : y; // NOTICE: May not need (_T)
	}
	//End Comparisons

	//Fast Math
	float fast_rand(void);
	float fast_sqrt(float x);
	float fast_invsqrt(float x);
	float __fastcall fast_invsqrtM$(float fValue);
	float fast_ln(float x);
	float fast_sin(float x);
	float fast_cos(float x);
	float fast_tan(float x);
	float fast_csc(float x);
	float fast_sec(float x);
	float fast_cot(float x);
	float fast_arcsin(float x);
	float fast_arccos(float x);
	float fast_arctan(float x);

	template <class _T>
	_T fast_abs(_T x)
	{
		return (_T) (x >= 0) ? x : -1*x;
	}

private:



};